import os
import re
import tempfile
import requests
import json
import socket


def check_package(package_name, install_name=None):
    try:
        __import__(package_name)
    except ImportError:
        if install_name:
            os.system(f'pip3 install {install_name}')
        else:
            os.system(f'pip3 install {package_name}')
        __import__(package_name)


def get_cpu_instruction_set():
    return os.popen('dpkg --print-architecture').read().strip()


def download_file(down_url):
    if 'github' in down_url and country in ['CN', 'cn', 'China', 'china']:
        down_url = 'https://ghproxy.com/' + down_url
    file_name = tempfile.mktemp()
    import wget
    wget.download(down_url, file_name)
    return file_name


def check_install_from_apt(package_name, version):
    is_new_install = False
    # 安装crictl
    print(f'检查 {package_name}')
    current_info = os.popen(f'apt-cache policy {package_name}').read()
    current_version = re.findall(r'Installed: (.*)', current_info)[0]
    if current_version == '(none)':
        print(f'安装{package_name}')
        os.system(f'apt-get install -y {package_name}={version}')
        is_new_install = True
    # 检查 {package_name} 版本
    print(f'检查 {package_name} 版本')
    current_info = os.popen(f'apt-cache policy {package_name}').read()
    current_version = re.findall(r'Installed: (.*)', current_info)[0]
    if current_version != version:
        print(f'{package_name} 版本不匹配')
        exit(1)
    return is_new_install


# 判断字符串是否包含在文件中
def contains(file, string):
    with open(file, 'r') as file_needle:
        for fileline in file_needle:
            if string in fileline:
                return True
    return False


in_aliyun = False
ip = ''
country = ''
do_what = 'init-kk'  # init, init-master, join-master, join-node, init-kk
containerd_version = '1.6.9'
containerd_apt_version = '1.6.9-1'
kubesphere_version = 'v3.3.0'
kubekey_version = 'v2.2.2'
k8s_kk_version = 'v1.22.10'
k8s_apt_version = '1.25.0-00'
k8s_version = '1.25.0'
nerdctl_version = 'v0.22.2'
calico_version = 'v3.24.0'
flannel_version = 'master'
cni_version = 'v1.1.1'
cri_tools_version = '1.24.2'
cri_tools_apt_version = '1.24.2-00'
ingress_version = 'v1.3.1'
network_type = 'intranet'  # intranet, extranet
network_plugin = 'calico'  # calico, flannel
is_single_node = True
# dry_run = '--dry-run --v=5'
dry_run = ''
join_address = ''
join_token = ''
join_cert = ''
region_id = ''
instance_id = ''
access_key = ''
secret_key = ''

if __name__ == '__main__':
    try:
        requests.get('http://100.100.100.200/latest/meta-data/region-id', timeout=0.1)
        in_aliyun = True
    except Exception as e:
        in_aliyun = False
        pass
    if in_aliyun:
        r = requests.get('http://100.100.100.200/latest/meta-data/region-id', timeout=1)
        region_id = r.text
        r = requests.get('http://100.100.100.200/latest/meta-data/instance-id', timeout=1)
        instance_id = r.text
        r = requests.get('http://100.100.100.200/latest/meta-data/eipv4', timeout=1)
        ip = r.text
        r = requests.get('http://100.100.100.200/latest/meta-data/private-ipv4', timeout=1)
        lip = r.text
    else:
        r = requests.get('http://ip-api.com/json/?fields=query,country')
        ip = r.json()['query']
        country = r.json()['country']
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            s.connect(('8.8.8.8', 80))
            lip = s.getsockname()[0]
        finally:
            s.close()
    # 初始化系统
    print('更新apt 列表')
    os.system('apt-get update 1> /dev/null 2>/dev/null')
    print('安装必要的软件包')
    packages = ['apt-transport-https', 'ca-certificates', 'curl', 'gnupg', 'lsb-release', 'dialog', 'socat',
                'conntrack', 'ebtables', 'ipset', 'python3-pip']
    for package in packages:
        print(f'检查 {package}')
        package_info = os.popen(f'apt-cache policy {package}').read()
        install_info = re.findall(r'Installed:\s+(.*)', package_info)[0]
        if install_info == '(none)':
            print(f'安装 {package}')
            os.system(f'apt-get install {package} -y 1> /dev/null 2>/dev/null')
    # 检查包是否安装,没有安装则安装然后引用
    check_package('wget')
    check_package('yaml', 'pyyaml')
    check_package('exrex')
    import yaml
    import exrex
    # 添加docker阿里云源
    need_apt_update = False
    if in_aliyun:
        print('检查 docker 阿里云源证书')
        if not os.path.exists('/usr/share/keyrings/ali-docker-archive-keyring.gpg'):
            print('添加docker阿里云源证书')
            os.system(
                'curl -fsSL http://mirrors.cloud.aliyuncs.com/docker-ce/linux/ubuntu/gpg |'
                ' gpg --dearmor -o /usr/share/keyrings/ali-docker-archive-keyring.gpg')
        print('检查 docker 阿里云源')
        if not os.path.exists('/etc/apt/sources.list.d/ali-docker.list'):
            print('添加docker阿里云源')
            os.system(
                'echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ali-docker-archive-keyring.gpg]'
                ' http://mirrors.cloud.aliyuncs.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" |'
                ' tee /etc/apt/sources.list.d/ali-docker.list')
            need_apt_update = True
        # 添加k8s阿里云源
        print('检查 k8s 阿里云源证书')
        if not os.path.exists('/usr/share/keyrings/ali-kubernetes-archive-keyring.gpg'):
            print('添加k8s阿里云源证书')
            os.system(
                'curl http://mirrors.cloud.aliyuncs.com/kubernetes/apt/doc/apt-key.gpg |'
                ' gpg --dearmor -o /usr/share/keyrings/ali-kubernetes-archive-keyring.gpg')
        print('检查 k8s 阿里云源')
        if not os.path.exists('/etc/apt/sources.list.d/ali-kubernetes.list'):
            print('添加k8s阿里云源')
            os.system(
                'echo "deb [arch=$(dpkg --print-architecture) '
                'signed-by=/usr/share/keyrings/ali-kubernetes-archive-keyring.gpg] '
                ' http://mirrors.cloud.aliyuncs.com/kubernetes/apt/ kubernetes-xenial main" |'
                ' tee /etc/apt/sources.list.d/ali-kubernetes.list')
            need_apt_update = True
    else:
        print('检查 docker 阿里云源证书')
        if not os.path.exists('/usr/share/keyrings/ali-docker-archive-keyring.gpg'):
            print('添加docker阿里云源证书')
            os.system(
                'curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg |'
                ' gpg --dearmor -o /usr/share/keyrings/ali-docker-archive-keyring.gpg')
        print('检查 docker 阿里云源')
        if not os.path.exists('/etc/apt/sources.list.d/ali-docker.list'):
            print('添加docker阿里云源')
            os.system(
                'echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ali-docker-archive-keyring.gpg]'
                ' http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" |'
                ' tee /etc/apt/sources.list.d/ali-docker.list')
            need_apt_update = True
        # 添加k8s阿里云源
        print('检查 k8s 阿里云源证书')
        if not os.path.exists('/usr/share/keyrings/ali-kubernetes-archive-keyring.gpg'):
            print('添加k8s阿里云源证书')
            os.system(
                'curl http://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg |'
                ' gpg --dearmor -o /usr/share/keyrings/ali-kubernetes-archive-keyring.gpg')
        print('检查 k8s 阿里云源')
        if not os.path.exists('/etc/apt/sources.list.d/ali-kubernetes.list'):
            print('添加k8s阿里云源')
            os.system(
                'echo "deb [arch=$(dpkg --print-architecture) '
                'signed-by=/usr/share/keyrings/ali-kubernetes-archive-keyring.gpg] '
                ' http://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main" |'
                ' tee /etc/apt/sources.list.d/ali-kubernetes.list')
            need_apt_update = True
    # 再次更新系统
    if need_apt_update:
        print('更新 apt 列表')
        os.system('apt-get update 1> /dev/null 2>/dev/null')
    # 获取虚拟内存信息
    print('检查 虚拟内存信息')
    swap_info = os.popen('free -m').read()
    swap_total = re.findall(r'Swap:\s+(\d+)', swap_info)[0]
    swap_used = re.findall(r'Swap:\s+\d+\s+(\d+)', swap_info)[0]
    swap_free = re.findall(r'Swap:\s+\d+\s+\d+\s+(\d+)', swap_info)[0]
    if not int(swap_total) == 0:
        print('关闭虚拟内存')
        os.system('swapoff -a')
    # 同步时间
    print('检查 时间同步')
    # 关闭防火墙
    print('检查 防火墙')
    os.popen('ufw status')
    if 'inactive' not in os.popen('ufw status').read():
        print('关闭防火墙')
        os.system('ufw disable')
    # 将服务器名称改为去全小写
    os.system('hostnamectl set-hostname ' + socket.gethostname().lower())
    # 安装containerd
    new_install = check_install_from_apt('containerd.io', containerd_apt_version)
    if new_install:
        # 设置containerd
        os.system('systemctl start containerd')
        os.system('systemctl enable containerd')
        # 验证containerd
        os.system('containerd -v')
        # 创建containerd配置文件
        # 创建目录
        if not os.path.exists('/etc/containerd'):
            os.makedirs('/etc/containerd', exist_ok=True)
        os.system('containerd config default | tee /etc/containerd/config.toml')
        needle = open('/etc/containerd/config.toml', 'r+')
        infos = needle.readlines()
        needle.seek(0, 0)
        for line in infos:
            line_new = line \
                .replace('sandbox_image = "k8s.gcr.io/pause:',
                         'sandbox_image = "registry.aliyuncs.com/google_containers'
                         '/pause:') \
                .replace('SystemdCgroup = false', 'SystemdCgroup = true')
            needle.write(line_new)
        needle.close()
        # 重启containerd
        os.system('systemctl restart containerd')
    if do_what == 'init-kk':
        # 获取home目录
        if not os.path.exists(os.path.join(os.path.expanduser('~'), 'kk')):
            # 获取kubekey版本
            r = requests.get('https://api.github.com/repos/kubesphere/kubekey/releases')
            json = r.json()
            name = ''
            url = ''
            for j in json:
                if j['tag_name'] == kubekey_version:
                    asset = j['assets']
                    for i in asset:
                        # 正则匹配kubekey版本
                        if re.match(r'kubekey-.*-linux-' + get_cpu_instruction_set() + '.tar.gz', i['name']):
                            name = i['name']
                            url = i['yesbrowser_download_url']
            if url != '':
                print('下载kubekey')
                kubekey_tmp = download_file(url)
                print('解压kubekey' + kubekey_tmp)
                os.system('tar Cxzvvf ~ ' + kubekey_tmp)
                os.system('chmod +x ~/kk')
                os.unlink(kubekey_tmp)
            else:
                print('未找到kubekey')
                exit(1)
        os.system(os.path.join(os.path.expanduser('~'), f'kk create config '
                                    f'--with-kubesphere {kubesphere_version} --with-kubernetes {k8s_kk_version}'))
        yamls = []
        kubekey_config_tmp = os.path.join(os.path.expanduser('~'), 'config-sample.yaml')
        with open(kubekey_config_tmp, encoding="utf-8") as f:
            datas = yaml.load_all(f, Loader=yaml.FullLoader)  # 将文件的内容转换为字典形式
            for data in datas:
                yamls.append(data)
        Cluster = yamls[0]
        ClusterConfiguration = yamls[1]


        with open(kubekey_config_tmp, 'w') as f:
            yaml.dump_all([Cluster, ClusterConfiguration], f)
        exit(0)
    # 加载br_netfilter模块
    print('检查 br_netfilter模块')
    if not (os.path.exists('/etc/modules-load.d/k8s.conf') and os.path.exists('/etc/modules-load.d/containerd.conf')):
        print('为K8s加载br_netfilter模块')
        with open('/etc/modules-load.d/k8s.conf', 'w') as f:
            f.writelines(['br_netfilter\n'])
        print('为containerd加载br_netfilter overlay2模块')
        with open('/etc/modules-load.d/containerd.conf', 'w') as f:
            f.writelines(['overlay\n'])
            f.writelines(['br_netfilter\n'])
        os.system('modprobe br_netfilter')
        os.system('modprobe overlay')
    # 配置内核参数
    print('检查 内核参数')
    if not os.path.exists('/etc/sysctl.d/k8s.conf'):
        print('配置内核参数')
        with open('/etc/sysctl.d/k8s.conf', 'w') as f:
            f.writelines([
                'net.bridge.bridge-nf-call-ip6tables = 1\n',
                'net.bridge.bridge-nf-call-iptables = 1\n',
                'net.ipv4.ip_forward = 1\n',
            ])
        os.system('sysctl --system 1> /dev/null 2>/dev/null')
    # 安装crictl
    check_install_from_apt('cri-tools', cri_tools_apt_version)
    # 安装nerdctl
    # https://api.github.com/repos/containerd/nerdctl/releases /usr/local/bin
    print('检查 nerdctl')
    if os.system('nerdctl -v 1> /dev/null 2>/dev/null') != 0:
        # 获取nerdctl版本
        r = requests.get('https://api.github.com/repos/containerd/nerdctl/releases')
        json = r.json()
        name = ''
        url = ''
        for j in json:
            if j['tag_name'] == nerdctl_version:
                asset = j['assets']
                for i in asset:
                    # 正则匹配nerdctl版本
                    if re.match(r'nerdctl-[0-9].*-linux-' + get_cpu_instruction_set() + '.tar.gz', i['name']):
                        name = i['name']
                        url = i['browser_download_url']
        if url != '':
            print('下载nerdctl')
            nerdctl_tmp = download_file(url)
            print('解压nerdctl' + nerdctl_tmp)
            os.system('tar Cxzvvf /usr/local/bin ' + nerdctl_tmp)
            os.unlink(nerdctl_tmp)
        else:
            print('未找到nerdctl')
            exit(1)
    print('检查 nerdctl 版本')
    current_nerdctl_info = os.popen('nerdctl -v').read()
    current_nerdctl_version = re.findall(r'nerdctl version (.*)', current_nerdctl_info)[0]
    if nerdctl_version != ('v' + current_nerdctl_version):
        print('nerdctl 版本不匹配')
        exit(1)
    # 安装k8s
    new_install = check_install_from_apt('kubelet', k8s_apt_version)
    if new_install:
        # 设置k8s
        os.system('systemctl start kubelet')
        os.system('systemctl enable kubelet')
    new_install = check_install_from_apt('kubeadm', k8s_apt_version)
    if new_install:
        needle = open('/etc/systemd/system/kubelet.service.d/10-kubeadm.conf', 'r+')
        infos = needle.readlines()
        needle.seek(0, 0)
        i = 0
        for line in infos:
            i += 1
            line_new = line
            if in_aliyun and ('$KUBELET_KUBECONFIG_ARGS $KUBELET_CLOUD_PROVIDER_ARGS' not in line):
                line_new = line_new \
                    .replace('/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS',
                             f'/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CLOUD_PROVIDER_ARGS')
            if network_type == 'external':
                line_new = line_new \
                    .replace('/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS',
                             f'/usr/bin/kubelet --node-ip {ip} $KUBELET_KUBECONFIG_ARGS')
            if i == 3:
                needle.write(
                    f'Environment="KUBELET_CLOUD_PROVIDER_ARGS='
                    f'--cloud-provider=external --provider-id={region_id}.{instance_id}"\n')
            needle.write(line_new)
        needle.close()
        os.system('systemctl restart kubelet')
        os.system('systemctl daemon-reload')
    check_install_from_apt('kubectl', k8s_apt_version)

    # # 创建虚拟网卡
    if network_type == 'external':
        os.system('ifconfig eth0:0 ' + ip + ' netmask 255.255.255.0 up')

    if do_what == 'init-master':
        # 初始化主节点
        # 获取 kubeadm init 默认配置文件到临时文件
        kubeadm_config_tmp = tempfile.mktemp()
        os.system('kubeadm config print init-defaults > ' + kubeadm_config_tmp)
        # 修改配置文件
        yamls = []
        with open(kubeadm_config_tmp, encoding="utf-8") as f:
            datas = yaml.load_all(f, Loader=yaml.FullLoader)  # 将文件的内容转换为字典形式
            for data in datas:
                yamls.append(data)
        InitConfiguration = yamls[0]
        ClusterConfiguration = yamls[1]
        InitConfiguration['bootstrapTokens'][0]['token'] = exrex.getone(r'[0-9a-f]{6}\.[0-9a-f]{16}')
        name = os.popen('hostname').read().strip().lower()
        InitConfiguration['nodeRegistration']['name'] = name
        if in_aliyun:
            InitConfiguration['patches'] = {}
            # 当前目录下的patches目录
            InitConfiguration['patches']['directory'] = os.path.join(os.getcwd(), 'patches')
        ClusterConfiguration['imageRepository'] = 'registry.aliyuncs.com/google_containers'
        if network_plugin == 'calico':
            ClusterConfiguration['networking']['podSubnet'] = '192.168.0.0/16'
        if network_plugin == 'flannel':
            ClusterConfiguration['networking']['podSubnet'] = '10.244.0.0/16'
        if network_type == 'intranet':
            ClusterConfiguration['controlPlaneEndpoint'] = ip + ':6443'
            InitConfiguration['localAPIEndpoint']['advertiseAddress'] = lip
        if network_type == 'external':
            ClusterConfiguration['controlPlaneEndpoint'] = ip + ':6443'
            InitConfiguration['localAPIEndpoint']['advertiseAddress'] = ip
            if 'certSANs' not in ClusterConfiguration['apiServer']:
                ClusterConfiguration['apiServer']['certSANs'] = []
            ClusterConfiguration['apiServer']['certSANs'].append(ip)
            ClusterConfiguration['apiServer']['certSANs'].append(lip)
            ClusterConfiguration['apiServer']['certSANs'].append('127.0.0.1')
            ClusterConfiguration['apiServer']['certSANs'].append('localhost')
            ClusterConfiguration['apiServer']['certSANs'].append('::1')
            if 'serverCertSANs' not in ClusterConfiguration['etcd']['local']:
                ClusterConfiguration['etcd']['local']['serverCertSANs'] = []
            ClusterConfiguration['etcd']['local']['serverCertSANs'].append(ip)
            ClusterConfiguration['etcd']['local']['serverCertSANs'].append(lip)
            ClusterConfiguration['etcd']['local']['serverCertSANs'].append('127.0.0.1')
            ClusterConfiguration['etcd']['local']['serverCertSANs'].append('localhost')
            ClusterConfiguration['etcd']['local']['serverCertSANs'].append('::1')
            if 'peerCertSANs' not in ClusterConfiguration['etcd']['local']:
                ClusterConfiguration['etcd']['local']['peerCertSANs'] = []
            ClusterConfiguration['etcd']['local']['peerCertSANs'].append(ip)
            ClusterConfiguration['etcd']['local']['peerCertSANs'].append(lip)
            ClusterConfiguration['etcd']['local']['peerCertSANs'].append('127.0.0.1')
            ClusterConfiguration['etcd']['local']['peerCertSANs'].append('localhost')
            ClusterConfiguration['etcd']['local']['peerCertSANs'].append('::1')
            ClusterConfiguration['etcd']['local']['extraArgs'] = {}
            # ClusterConfiguration['etcd']['local']['extraArgs']['name'] = name
            # ClusterConfiguration['etcd']['local']['extraArgs']['initial-cluster-state'] = 'new'
            # ClusterConfiguration['etcd']['local']['extraArgs']['initial-cluster'] = f'{name}=https://{ip}:2380'
            ClusterConfiguration['etcd']['local']['extraArgs'][
                'listen-peer-urls'] = f'https://0.0.0.0:2380'
            ClusterConfiguration['etcd']['local']['extraArgs'][
                'listen-client-urls'] = f'https://0.0.0.0:2379'
            # ClusterConfiguration['etcd']['local']['extraArgs'][
            #     'advertise-client-urls'] = f'https://{ip}:2379'
            # ClusterConfiguration['etcd']['local']['extraArgs'][
            #     'initial-advertise-peer-urls'] = f'https://{ip}:2380'
        with open(kubeadm_config_tmp, 'w') as f:
            yaml.dump_all([InitConfiguration, ClusterConfiguration], f)
        run_tmp = tempfile.mktemp()
        run = os.system(f'kubeadm init --config {kubeadm_config_tmp} --upload-certs {dry_run} | tee {run_tmp}')
        if contains(run_tmp, 'Your Kubernetes control-plane has initialized successfully!') and dry_run == '':
            print('k8s 初始化成功')
            os.system('mkdir -p $HOME/.kube')
            os.system('cp -i /etc/kubernetes/admin.conf $HOME/.kube/config')
            os.system('chown $(id -u):$(id -g) $HOME/.kube/config')
            os.system('export KUBECONFIG=/etc/kubernetes/admin.conf')
            if network_plugin == 'calico':
                tigera = download_file(
                    f'https://raw.githubusercontent.com/projectcalico/calico/{calico_version}/manifests/tigera-operator'
                    f'.yaml')
                custom = download_file(
                    f'https://raw.githubusercontent.com/projectcalico/calico/{calico_version}/manifests/custom-resources'
                    f'.yaml')
                os.system('kubectl create -f ' + tigera)
                os.system('kubectl create -f ' + custom)
            if network_plugin == 'flannel':
                flannel = download_file(
                    f'https://raw.githubusercontent.com/flannel-io/flannel/{flannel_version}/Documentation/kube-flannel'
                    f'.yml')
                os.system('kubectl create -f ' + flannel)
            if is_single_node:
                os.system(f'kubectl taint nodes {name} node-role.kubernetes.io/control-plane-')
            ingress = download_file(f'https://raw.githubusercontent.com/kubernetes/ingress-nginx/'
                                    f'controller-{ingress_version}/deploy/static/provider/cloud/deploy.yaml')
            os.system('kubectl apply -f ' + ingress)
    if do_what == 'join-master':
        # join 主节点
        # 获取 kubeadm init 默认配置文件到临时文件
        kubeadm_config_tmp = tempfile.mktemp()
        os.system('kubeadm config print join-defaults > ' + kubeadm_config_tmp)
        # 修改配置文件
        import yaml
        import exrex

        yamls = []
        with open(kubeadm_config_tmp, encoding="utf-8") as f:
            datas = yaml.load_all(f, Loader=yaml.FullLoader)  # 将文件的内容转换为字典形式
            for data in datas:
                yamls.append(data)
        JoinConfiguration = yamls[0]
        print(yamls)
